package org.triumphxx.util;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * @author:triumphxx
 * @Date: 2024/1/8
 * @Time: 15:31
 * @微信公众号：思考集结处
 * @网站：http://blog.triumphxx.com.cn
 * @GitHub https://github.com/triumphxx
 * @Desc: 对象属性合并工具
 **/

public class ObjectMerger {

    public static <T> T mergeFields(Object... objects) throws IllegalAccessException, InstantiationException {
        if (objects == null || objects.length <= 0) {
            return null;
        }
        Class<?> targetClass = objects[0].getClass();
        Field[] fields = targetClass.getDeclaredFields();
        for (int i = 1; i < objects.length; i++) {
            Field[] otherFields = objects[i].getClass().getDeclaredFields();

            for (Field field : otherFields) {
                try {
                    field.setAccessible(true);
                    Field existingField = findExistingField(fields, field.getName());

                    if (existingField != null && !field.equals(existingField)) {
                        throw new RuntimeException("Duplicate field name found in the merged object.");
                    } else {
                        fields = appendToArray(fields, field);
                    }
                } catch (Exception e) {
                    continue;
                }
            }
        }

        return (T) createNewInstanceWithFields(targetClass, fields);
    }

    private static Field findExistingField(Field[] fields, String fieldName) {
        for (Field field : fields) {
            if (field.getName().equalsIgnoreCase(fieldName)) {
                return field;
            }
        }

        return null;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private static Field[] appendToArray(Field[] array, Field element) {
        int length = array.length + 1;
        Field[] result = Arrays.copyOf(array, length);
        result[length - 1] = element;
        return result;
    }

    private static Object createNewInstanceWithFields(Class<?> clazz, Field[] fields) throws InstantiationException, IllegalAccessException {
        Object instance = clazz.newInstance();

        for (Field field : fields) {
            field.setAccessible(true);
            field.set(instance, getDefaultValueForType(field));
        }

        return instance;
    }

    private static Object getDefaultValueForType(Field field) {
        Class<?> type = field.getType();
        if (type == boolean.class) {
            return false;
        } else if (type == byte.class) {
            return (byte) 0;
        } else if (type == short.class) {
            return (short) 0;
        } else if (type == char.class) {
            return '\u0000';
        } else if (type == int.class) {
            return 0;
        } else if (type == long.class) {
            return 0L;
        } else if (type == float.class) {
            return 0F;
        } else if (type == double.class) {
            return 0D;
        } else {
            return null;
        }
    }
}
